package com.hgh.util;

import com.hgh.model.EnemyTank;
import com.hgh.model.Map;
import com.hgh.model.Tank;
import com.misakubex.BTFrame;
import com.misakubex.Main;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 用A*算法寻找到达基地的最短路径
 * @author hgh
 */
public class EnemyTankMoveAstar implements Runnable{

    public static java.util.Map<Integer,List<Node>> beatPathMap = new HashMap<Integer,List<Node>>();

    //节点扩展的方向
    public static int[][] expend = {
            {1,0},{0,1},{-1,0},{0,-1}
    };


    private EnemyTank tank;

    public EnemyTankMoveAstar(EnemyTank tank){
        this.tank = tank;
    }

    /**
     * 标记最短路径
     * @param end
     */
    public static void setPath(Node end,int bornMark){
        Node father = end.getFather();
        List<Node> pointList = new ArrayList<Node>();
        pointList.add(end);
        while(father!=null){
            pointList.add(father);
            father = father.getFather();
        }
        Collections.reverse(pointList);
        beatPathMap.put(bornMark,pointList);
    }

    /**
     * 估计节点到终点的耗散值
     * @param x
     * @param y
     * @return h
     */
    public static int getH(int x,int y, int[] targetPoint){
        return Math.abs(x-targetPoint[0])+Math.abs(y-targetPoint[1]);
    }

    /**
     * 将open中的节点按照f值从小到大排序
     * @param nodes
     * @return nodes
     */
    public static List<Node> arrangeOpen(List<Node> nodes){
        Collections.sort(nodes, new Comparator<Node>(){
            @Override
            public int compare(Node o1, Node o2) {
                int f1 = o1.getG()+o1.getH();
                int f2 = o2.getG()+o2.getH();
                if(f1>f2){
                    return 1;
                }
                else if(f1<f2){
                    return -1;
                }
                else {
                    return 0;
                }
            }
        });
        return nodes;
    }

    /**
     * 判断节点列表中是否有节点在（x，y）位置
     * @param x
     * @param y
     * @return boolean
     */
    public static Node ifContains(List<Node> nodes, int x, int y){
        for(Node node : nodes){
            if(node.getX()==x&&node.getY()==y){
                return node;
            }
        }
        return null;
    }

    /**
     * 判断两个点是否相邻
     */
    public boolean isClosed(Node a, Node b){
        return (Math.abs(a.getX()-b.getX())+Math.abs(a.getY()-b.getY()))==1?true:false;
    }



    public void findBestPath() throws InterruptedException {
        List<Node> open = new ArrayList<Node>();
        List<Node> close = new ArrayList<Node>();
        Node start = new Node(tank.getX()/ Main.a,tank.getY()/Main.a);
        start.setG(0);
        start.setH(getH(tank.getX()/Main.a,tank.getY()/Main.a,tank.getTargetPoint()));
        open.add(start);
        Node currentNode = null;
        while(!open.isEmpty()){
            //如果坦克已死，销毁线程
            if(!EnemyTank.isAlive(tank)){
                return;
            }
            currentNode = open.remove(0);
            close.add(currentNode);
            if(currentNode.getY()==Map.base[1]&&currentNode.getX()==Map.base[0]){
                break;
            }
            //从当前位置到要去的下一个节点的位置
            tankMoveFromOnePointToAnotherPoint(new Node(tank.getX()/Main.a,tank.getY()/Main.a),currentNode);
            //扩展节点
            expansionNode(open,close,currentNode);
            open = arrangeOpen(open);
        }
        setPath(currentNode,tank.getBornMark());
    }

    /**
     * A*算法中扩展节点的步骤
     * @param open
     * @param close
     * @param currentNode
     */
    public void expansionNode(List<Node> open,List<Node> close,Node currentNode){
        //扩展节点
        for(int i=0;i<4;i++){
            int x = currentNode.getX()+expend[i][0];
            int y = currentNode.getY()+expend[i][1];
            if(x<0||y<0||x>=Map.WIDTH||y>=Map.HEIGHT){
                continue;
            }
            //如果是墙壁则不走
            if(Map.points[x][y]==Map.REDWALL||Map.points[x][y]==Map.WHITEWALL){
                continue;
            }
            Node inOpen = ifContains(open,x,y);
            Node inClose = ifContains(close,x,y);
            int newG = currentNode.getG()+1;
            if(inOpen==null && inClose==null){
                Node expendNode = new Node(x,y);
                expendNode.setG(newG);
                expendNode.setH(getH(x,y,tank.getTargetPoint()));
                expendNode.setFather(currentNode);
                open.add(expendNode);
            }
            else if(inOpen!=null){
                if(inOpen.getG()>newG){
                    inOpen.setG(newG);
                    inOpen.setFather(currentNode);
                }
            }
        }
    }

    /**
     * 获得最短路径
     * @param end
     */
    public static List<Node> setPath(Node end){
        List<Node> list = new ArrayList<Node>();
        list.add(end);
        Node father = end.getFather();
        while(father!=null){
            list.add(father);
            father = father.getFather();
        }
        return list;
    }


    public boolean isCanMove(Node node){
        for(EnemyTank e : BTFrame.enemyTankList1){
            if(e.getX()==node.getX()*Main.a&&e.getY()==node.getY()*Main.a){
                return false;
            }
        }
        if(BTFrame.tLocal.ta.getX()==node.getX()*Main.a&&BTFrame.tLocal.ta.getY()==node.getY()*Main.a){
            return false;
        }
        return true;
    }

    /**
     * 坦克从一个点向另一个点前进
     * @param start
     * @param end
     * @throws InterruptedException
     */
    public void tankMoveFromOnePointToAnotherPoint(Node start,Node end) throws InterruptedException {
        //判断当前位置和要去的位置是否相邻，相邻则直接走过去，否则用A*算法找最近的路过去
        if(isClosed(start,end)){
            setTankDirection(start,end);
            //tank.move();
            while(!isCanMove(end)){
                TimeUnit.MILLISECONDS.sleep(250);
            }
            tank.setX(end.getX()*Main.a);
            tank.setY(end.getY()*Main.a);
            TimeUnit.MILLISECONDS.sleep(500);
            return ;
        }
        List<Node> open = new ArrayList<Node>();
        List<Node> close = new ArrayList<Node>();
        start.setG(0);
        start.setH(getH(start.getX(),start.getY(),new int[]{end.getX(),end.getY()}));
        open.add(start);
        Node currentNode = null;
        while(!open.isEmpty()){
            currentNode = open.remove(0);
            close.add(currentNode);
            if(currentNode.getY()==end.getY()&&currentNode.getX()==end.getX()){
                break;
            }
            expansionNode(open,close,currentNode);
            open = arrangeOpen(open);
        }
        List<Node> path =  setPath(currentNode);
        //根据找到的最短路径走过去
        for(int i=path.size()-1;i>0;i--){
            setTankDirection(new Node(tank.getX()/Main.a,tank.getY()/Main.a),path.get(i));
            while(!isCanMove(end)){
                TimeUnit.MILLISECONDS.sleep(250);
            }
            //tank.move();这里不知道为什么不行，改用下面的方式直接设置坦克的位置就才可以
            tank.setX(path.get(i).getX()*Main.a);
            tank.setY(path.get(i).getY()*Main.a);
            TimeUnit.MILLISECONDS.sleep(500);
            if(path.get(i).getX()==Map.base[0]&&path.get(i).getY()==Map.base[1]){
                break;
            }
        }
    }

    /**
     * 敌机坦克移动的时候的方向
     * @param start
     * @param end
     */
    public void setTankDirection(Node start,Node end){
        if(end.getY()-start.getY()!=0){
            if(end.getY()-start.getY()>0){
                tank.setDirct('s');
            }
            else{
                tank.setDirct('w');
            }
        }
        else{
            if(end.getX()-start.getX()>0){
                tank.setDirct('d');
            }
            else{
                tank.setDirct('a');
            }
        }
    }


    /**
     * 按照前面敌机找到的最佳路径移动
     * @param aimPath
     * @throws InterruptedException
     */
    public void moveWithBestPath(List<Node> aimPath) throws InterruptedException {
        for(Node point : aimPath){
            setTankDirection(new Node(tank.getX()/Main.a,tank.getY()/Main.a),point);
            while(!isCanMove(point)){
                TimeUnit.MILLISECONDS.sleep(250);
            }
            tank.setX(point.getX()*Main.a);
            tank.setY(point.getY()*Main.a);
            TimeUnit.MILLISECONDS.sleep(500);
        }
    }

    @Override
    public void run() {
        List<Node> aimPath = beatPathMap.get(tank.getBornMark());
        try {
            if(aimPath!=null){
                moveWithBestPath(aimPath);
            }
            else{
                findBestPath();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}